package com.googlecode.imanager.swing;

import java.awt.Dialog;
import java.awt.Frame;
import java.awt.Insets;
import java.awt.Window;
import java.awt.Dialog.ModalityType;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JDialog;
import javax.swing.JFrame;

import com.googlecode.imanager.client.Container;
import com.googlecode.imanager.client.Control;
import com.googlecode.imanager.client.Document;
import com.googlecode.imanager.client.Manager;
import com.googlecode.imanager.client.bridge.IDocument;
import com.googlecode.imanager.client.bridge.IFocusable;
import com.googlecode.imanager.client.event.EvaluationPhase;
import com.googlecode.imanager.client.style.PageSettings;
import com.googlecode.imanager.client.style.Style;
import com.googlecode.imanager.core.base.BridgeApplication;
import com.googlecode.imanager.core.base.style.ComputedTheme;
import com.googlecode.imanager.swing.layout.SwingScrollBox;

/**
 * @author wellsantos@wellsantos.com
 * @since 0.01
 */
public class SwingDocument extends SwingScrollBox implements IDocument {
    
    private static final int NO_IMPLEMENTATION = 0;
    private static final int USING_DOCUMENT = 1;
    private static final int USING_WINDOW = 2;

    private int usingWindow;
    private Window targetWindow;
    private Insets insets;    
    private int lastWidth;
    private int lastHeight;
    private boolean notifyChanged;
    private ComputedTheme computedTheme;
    
    public SwingDocument(BridgeApplication bridge, Document source) {
        super(bridge, source);
        //target.setBackground(Color.WHITE);
    }

    @Override
    public Document getSource() {
        return (Document) source;
    }

    protected Window getTargetWindow(){
        return targetWindow;
    }
    
    public java.awt.Container getTargetWrapper(){
        if (isUsingWindow()){
            return targetWindow;
        }
        return (java.awt.Container) target;
    }
    
    public boolean isUsingWindow() {
        return usingWindow == USING_WINDOW;
    }
    
    @Override
    protected void buildImplementation() {
        computedTheme = new ComputedTheme(getSource());
        super.buildImplementation();
    }

    @Override
    protected void destroy() {
        setVisible(false);
        super.destroy();
        usingWindow = NO_IMPLEMENTATION;
        
        if (targetWindow != null
                && targetWindow.isShowing()){
            targetWindow.setVisible(false);
        }
        
        targetWindow = null;
    }

    @Override
    public ComputedTheme getComputedTheme() {        
        return computedTheme;
    }

    @Override
    public void setParent(Container parent) {
        super.setParent(parent);
        setVisible(getSource().isVisible());
    }

    @Override
    public void setBoundsExt(int x, int y, int width, int height) {
        if (isUsingWindow()){
            lastWidth = width;
            lastHeight = height;
            int w = width;
            int h = height;

            if (insets != null){
                w += insets.left + insets.right;
                h += insets.top + insets.bottom;
            }
            
            super.setBoundsExt(0, 0, width, height);

            getTargetWindow().setBounds(x, y, w, h);
            getTargetWindow().validate();
        } else {
            super.setBoundsExt(x, y, width, height);
        }
    }

    @Override
    public final void setVisible(Boolean visible) {
        if (isRunning()){
            validateWindow();
        }
    }
    
    public void setTitle(String title) {
        if (isUsingWindow()){
            if (targetWindow instanceof Dialog){
                ((Dialog)targetWindow).setTitle(title);
            }
            else if (targetWindow instanceof Frame) {
                ((Frame)targetWindow).setTitle(title);
            }
        }
    }

    public void setModal(boolean modal) {
        if (isUsingWindow()
                || isInitializing()){
            validateWindow();
        }
    }

    public void scrollToPage(int pageNo) {
        // TODO Auto-generated method stub
    }
    
    public void setGlobalStyle(Class<? extends Control> type, String prop, Style style) {
        // TODO Auto-generated method stub
    }

    
    private void validateWindow() {
        int oldFlag = usingWindow;
        boolean sourceVisible = getSource().isVisible();        
        usingWindow = sourceVisible && !getSource().hasParent() ? USING_WINDOW : USING_DOCUMENT;
        boolean reallocated = (usingWindow == USING_WINDOW) != (oldFlag == USING_WINDOW);
        notifyChanged = notifyChanged || reallocated;
        boolean isModal = getSource().isModal();

        
        if (isUsingWindow()){
            synchronized (this) {
                if (targetWindow != null){
                    boolean wasModal = targetWindow instanceof Dialog;
                    
                    if (isModal != wasModal){
                        targetWindow.remove(target);
                        targetWindow.setVisible(false);
                        targetWindow = null;
                    }
                }
                if (targetWindow == null){
                    if (getSource().isModal()){
                        targetWindow = new JDialog(getBridgeApplication().getTopLevelWindow(), ModalityType.APPLICATION_MODAL);
                    } else {
                        targetWindow = new JFrame();
                    }
                    doAttachWindowEvents();
                    reallocated = true;
                }
            }
        }
        
        if (oldFlag == NO_IMPLEMENTATION){
            getManager().fireParentResized(getSource());
        }
        
        if (reallocated){
            if (isUsingWindow()){
                targetWindow.add(target);
                target.setVisible(true);
            } else {
                targetWindow.remove(target);
                targetWindow.setVisible(false);
            }
            
            setTitle(getSource().getTitle());
            setBoundsExt(source.getLeft(), source.getTop(), source.getWidth(), source.getHeight());
            
            if (!isUsingWindow() 
                    || !sourceVisible
                    /*|| !isModal*/){
                
                getTargetWrapper().setVisible(sourceVisible);
                
                if (!isUsingWindow()){
                    if (notifyChanged){
                        notifyChanged = false;
                        getBridgeApplication().firePhaseEvent(getSource(), EvaluationPhase.WINDOW_HIDE);
                    }
                }
                return;
            }

            if (oldFlag == NO_IMPLEMENTATION){
                getBridgeApplication().addDocumentToUpdate(this);
                return;
            }   
            
            // maybe we are creating another document implementation
            if (!getBridgeApplication().addDocumentToUpdateIfProcessing(this)){

                // no, we aren't creating another one
                updateDocumentVisibility();
                return;
            }            
        } else {
            getTargetWrapper().setVisible(sourceVisible);
        }
    }

    public void updateDocumentVisibility() {
        synchronized (TREE_LOCK) {
            if (!isDropped()){
                getTargetWindow().setVisible(true);
            }
        }
        
        // FIXME dialogos modais AWT bloqueiam a thread
        //       porém esse não é o comportamento desejado, já que
        //       outras implementações podem não dar suporte a isso
    }

    protected void doAttachWindowEvents() {
        final Window window = targetWindow;
        
        window.addWindowListener(new WindowAdapter() {   
            
            @Override
            public void windowActivated(WindowEvent e) {
                synchronized (TREE_LOCK) {
                    if (targetWindow == window
                            && isUsingWindow()){
                        window.repaint();
                    }
                }
            }

            @Override
            public void windowOpened(WindowEvent e) {
                synchronized (TREE_LOCK) {
                    if (targetWindow == window
                            && isUsingWindow()){
                        startFireUIEvent();
                        try {
                            if (insets == null){
                                insets = window.getInsets();
                                window.setSize(
                                        window.getWidth() + insets.left + insets.right, 
                                        window.getHeight() + insets.top + insets.bottom);
                                SwingDocument.this.validateChildrenInsets();
                            }
                            if (notifyChanged){
                                notifyChanged = false;
                                getBridgeApplication().firePhaseEvent(getSource(), EvaluationPhase.WINDOW_SHOW);
                                
                                Control focused = (Control) source.getApplication().getFocusControl();
                                if (focused != null
                                        && focused.getRootDocument() == source){
                                    IFocusable f = (IFocusable) Manager.getImpl(focused);
                                    f.focus();
                                }
                            }
                        } catch (Exception ex) {
                            processException(ex);
                        } finally {
                            endFireUIEvent();
                        }
                    }
                }
            }

            @Override
            public void windowClosing(WindowEvent e) {
                synchronized (TREE_LOCK) {
                    if (targetWindow == window
                            && isUsingWindow()){
                        startFireUIEvent();
                        try {
                            getSource().setVisible(false);
                        } catch (Exception ex) {
                            processException(ex);
                        } finally {
                            endFireUIEvent();
                        }
                    }
                }
            }
        });
        
        window.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                synchronized (TREE_LOCK) {
                    if (targetWindow == window
                            && isUsingWindow()
                            && insets != null){
                        startFireUIEvent();
                        try {
                            int w = window.getWidth() - (insets.left + insets.right);
                            int h = window.getHeight() - (insets.top + insets.bottom);
                            
                            boolean sizeChanged = lastWidth != w 
                                               || lastHeight != h;
                            
                            if (updatingSize == 0){                            
                                if (sizeChanged){
                                    
                                    getSource().setAnchors(null);
                                    getSource().setBounds(window.getX(), window.getY(), w, h);
                                }
                            }
    
                            lastWidth = w;
                            lastHeight = h;
                        } catch (Exception ex) {
                            processException(ex);
                        } finally {
                            endFireUIEvent();
                        }
                    }
                }
            }
        });        
    }

    @Override
    public void setPageSettings(PageSettings pageSettings) {
        // TODO Auto-generated method stub        
    }

}
